﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="StoreShould.cs" company="SaltFx">
//   Copyright (c) SaltFx - All rights reserved.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace SaltFx.Tests.LocalData
{
	using System;
	using System.Threading;

	using NUnit.Framework;

	/// <summary>
	/// The Local.Data storage tests.
	/// </summary>
	public class StoreShould
	{
		#region Constants and Fields

		/// <summary>
		/// The manual reset event.
		/// </summary>
		private ManualResetEvent manualResetEvent;

		#endregion

		#region Public Methods

		/// <summary>
		/// Test to make sure we can store a string value in local data.
		/// </summary>
		/// <param name="key">
		/// The key to store.
		/// </param>
		/// <param name="value">
		/// The value to store.
		/// </param>
		[TestCase("key", "value")]
		[TestCase("reasonable_string", "This is a reasonable looking string.")]
		[TestCase("single_char", "x")]
		[TestCase("spaces_only", "     ")]
		[TestCase("empty_string", "")]
		public void StoreLocalDataGivenVariousDataTypes(string key, string value)
		{
			// Arrange

			// Act
			Local.Data[key] = value;

			// Assert
			Assert.AreEqual(value, Local.Data[key]);
		}

		/// <summary>
		/// Test to ensure that local data is thread local.
		/// </summary>
		[Test]
		public void StoreLocalDataPerThread()
		{
			// Arrange
			Console.WriteLine("Starting in main thread {0}", Thread.CurrentThread.ManagedThreadId);
			Local.Data.Clear();

			// Act
			Local.Data["one"] = "This is a string";
			Assert.AreEqual(1, Local.Data.Count);
			this.manualResetEvent = new ManualResetEvent(false);
			var backgroundThread = new Thread(this.RunInOtherThread);
			backgroundThread.Start();
			Thread.Sleep(100);

			// Assert that we still have only one entry (in this thread)...
			Assert.AreEqual(1, Local.Data.Count);

			Console.WriteLine("Signaling background thread from main thread {0}", Thread.CurrentThread.ManagedThreadId);
			this.manualResetEvent.Set();
			backgroundThread.Join();
		}

		/// <summary>
		/// Test to make sure we can store multiple types in local data.
		/// </summary>
		public void StoreVariousDataTypesConcurrently()
		{
			// Arrange

			// Act
			Local.Data["one"] = "This is a string";
			Local.Data["two"] = 99.9m;

			var someObject = new object();
			Local.Data[1] = someObject;

			// Assert
			Assert.AreEqual(3, Local.Data.Count);
			Assert.AreEqual("This is a string", Local.Data["one"]);
			Assert.AreEqual(99.9m, Local.Data["two"]);
			Assert.AreSame(someObject, Local.Data[1]);
		}

		#endregion

		#region Methods

		/// <summary>
		/// Creates a Local.Data instance in another thread.
		/// </summary>
		private void RunInOtherThread()
		{
			Console.WriteLine("Starting thread {0}", Thread.CurrentThread.ManagedThreadId);

			Local.Data.Clear();

			// Assert that local data is empty for this new thread...
			Assert.AreEqual(0, Local.Data.Count);

			Local.Data["one"] = "This is another string";

			Assert.AreEqual(1, Local.Data.Count);

			Console.WriteLine("Waiting on thread {0}", Thread.CurrentThread.ManagedThreadId);

			this.manualResetEvent.WaitOne();

			Console.WriteLine("Ending thread {0}", Thread.CurrentThread.ManagedThreadId);
		}

		#endregion
	}
}